//! 全球唯一ID

use crate::{Id, IdKind, ID_LEN};
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;

#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Debug, PartialEq)]
pub struct UniqueId {
    product: u64,
    sn: u64,
}
#[cfg_attr(feature = "wasm", wasm_bindgen)]
impl UniqueId {
    pub const fn new(product: u64, sn: u64) -> Self {
        return UniqueId { product, sn };
    }
    pub const fn product(&self) -> u64 {
        self.product
    }
    pub const fn sn(&self) -> u64 {
        self.sn
    }
    pub const fn id(&self) -> Id {
        let product = self.product.to_be_bytes();
        let sn = self.sn.to_be_bytes();
        let mut i_p = 0;
        while i_p < product.len() && product[i_p] == 0 {
            i_p += 1;
        }
        let product_len = product.len() - i_p;
        let mut i_s = 0;
        while i_s < sn.len() && sn[i_s] == 0 {
            i_s += 1;
        }
        let sn_len = sn.len() - i_s;

        let mut buf: [u8; ID_LEN] = [0; ID_LEN];
        buf[0] = 0x80 | (product_len + sn_len) as u8;
        buf[1] = ((sn_len << 4) | product_len) as u8;
        let mut i = 0;
        while i < product_len {
            buf[i + 2] = product[i_p + i];
            i += 1;
        }
        let mut i = 0;
        while i < sn_len {
            buf[i + 2 + product_len] = sn[i_s + i];
            i += 1;
        }
        Id {
            len: 2 + product_len + sn_len,
            buf: buf,
        }
    }
    pub fn try_from(id: &Id) -> Option<UniqueId> {
        if id.kind() != IdKind::Unique {
            return None;
        }
        let product_len = id.buf[1] & 0x0F;
        let sn_len = (id.buf[1] & 0xF0) >> 4;
        if id.len() as u8 != 2 + product_len + sn_len || sn_len > 7 {
            return None;
        }
        let mut product: [u8; 8] = [0; 8];
        let mut sn: [u8; 8] = [0; 8];
        product[8 - product_len as usize..].copy_from_slice(&id.buf[2..2 + product_len as usize]);
        sn[8 - sn_len as usize..].copy_from_slice(&id.buf[2 + product_len as usize..id.len()]);
        return Some(UniqueId::new(
            u64::from_be_bytes(product),
            u64::from_be_bytes(sn),
        ));
    }
}
